H i t ' n ' M i x   R i p S c r i p t       @1 . 0 4   L o o p   S e l e c t i o n   ]  r u n . p y   """
Description: Loop selection to selected rips or new rip
Category: Tools
Shortcut: 
Level: Intermediate
Version: 1.04
"""

def ripscript():
	
	# Load settings and set defaults
	settings = Settings()
	settings.add("loops", "16")
	settings.add("bars_per_loop", "1")
	settings.add("looprange", "time") # Loop selected "time" vs "bar"
	settings.add("contents", "selection") # Loop "selection" vs "all"

	# Add a window
	window = infinity.add_tk_window()
	pad_x = 8
	pad_y = 6
	
	label = window.add_label(text="1) Select rip(s) to add loops to, placing cursor at start bar.\n   If no rips are selected a new one will be created.\n2) Select notes/time to loop, choose settings below & click Loop.")
	label.grid(column=0, row=0, columnspan=2, padx=pad_x, pady=pad_y, sticky="we")
	
	# Add frame to place widgets in
	frame = window.add_label_frame(text="Number of loops")
	frame.grid(column=0, row=1, padx=12, pady=8, sticky="we")
	loops_entry = frame.add_entry(textvariable=loops, width=4)
	loops_entry.grid(column=0, row=0, padx=pad_x, pady=pad_y, sticky="we")

	frame = window.add_label_frame(text="Bars per loop")
	frame.grid(column=1, row=1, padx=12, pady=8, sticky="we")
	bars_entry = frame.add_entry(textvariable=bars_per_loop, width=4)
	bars_entry.grid(column=0, row=0, padx=pad_x, pady=pad_y, sticky="we")

	frame = window.add_label_frame(text="Range to loop")
	frame.grid(column=0, row=2, padx=12, pady=8, sticky="we")
	range_button_time = frame.add_radio_button(text="Selected time", variable=looprange, value="time")
	range_button_time.grid(column=0, row=0, padx=pad_x, sticky=(W))
	range_button_bar = frame.add_radio_button(text="Selected bars", variable=looprange, value="bar")
	range_button_bar.grid(column=0, row=1, padx=pad_x, pady=pad_y, sticky=(W))	

	frame = window.add_label_frame(text="Contents to loop")
	frame.grid(column=1, row=2, padx=12, pady=8, sticky="we")
	contents_button_time = frame.add_radio_button(text="Selected notes only", variable=contents, value="selection")
	contents_button_time.grid(column=0, row=1, padx=pad_x, sticky=(W))
	contents_button_bar = frame.add_radio_button(text="All notes in range", variable=contents, value="all")
	contents_button_bar.grid(column=0, row=2, padx=pad_x, pady=pad_y, sticky=(W))	
	
	def apply_loop(event: Event):

		# Show messages if rip not open or nothing selected (optional)
		rip = infinity.current_view.rip
		if rip is None:
			infinity.pop_up("Please open a rip first")
			return
			
		# Valid number of loops entered?
		nloops = int(loops.get())
		if nloops < 1 or nloops > 1000:
			infinity.pop_up("Please enter between 1 and 1000 loops")
			return
				
		# Are notes/time selected?
		if len(rip.selected_notes) == 0 and (rip.selected_bar_range is None or rip.selected_bar_range.last < 1):
			infinity.pop_up("Please select notes/time to loop in current rip")
			return

		bpl = int(bars_per_loop.get())

		# Go through each selected rip
		if len(infinity.riplist.selected_rips) == 0:
			# Create rip to loop to and select
			new_rip = infinity.riplist.new_rip(file=rip.name+" (loop)")
			# Set tempo to same as source
			bar_duration = 0
			if looprange.get() == "bar":
				source_start_bar = rip.bar_at(time=rip.selected_time_range.start)
				if source_start_bar is not None:
					bar_duration = source_start_bar.duration
			else:	# "time"
				bar_duration = rip.selected_time_range.duration / bpl
			if bar_duration > 0:
				new_rip.bar(number=1).stretch(duration=bar_duration)
			rips = [new_rip]
		else:
			rips = infinity.riplist.selected_rips
		
		if contents.get() == "selection":
			# Get selected notes
			if len(rip.selected_notes) == 0:
				selected_notes = rip.selected_time_range.notes
			else:
				selected_notes = rip.selected_notes
				
		for to_rip in rips:
			
			# What bar index to copy to?
			start_bar_index = 1
			if to_rip == rip:
				# If copying to same rip, we place from bar following selection
				if to_rip.selected_time_range is not None :
					start_bar = to_rip.bar_at(time=to_rip.selected_time_range.end)
					if start_bar is not None:
						# Recalculate using bar duration to prevent slight overlaps into bar
						# causing it to skip it
						start_bar = to_rip.bar_at(time=to_rip.selected_time_range.end - start_bar.duration / 12)
						# Choose following bar
						start_bar_index = start_bar.number + 1
						start_bar = to_rip.bar(number=start_bar_index)
				
			else:
				if to_rip.selected_time_range is not None :
					start_bar = to_rip.bar_at(time=to_rip.selected_time_range.start)
					if start_bar is not None:
						start_bar_index = start_bar.number
			
			# Copy loops
			for loop_index in range(0, nloops):
				
				# Get start start and duration of loop
				to_bar_start = to_rip.bar(start_bar_index+loop_index*bpl).start
				to_bar_end = to_rip.bar(start_bar_index+loop_index*bpl+bpl-1).end
				to_bar_duration = to_bar_end - to_bar_start
				
				# Store original number of bars as may need to resize any to correct duration added to store copied notes
				orig_n_bars = len(to_rip.bars)
	
				# Copy bars
				if looprange.get() == "bar":
					if contents.get() == "all":
						new_notes = rip.bar(number=rip.selected_bar_range.first).notes.copy_to(to_bar_start, to_rip)
						new_notes.stretch(duration=to_bar_duration)
					else:	# "selection"
						new_notes = selected_notes.copy_to(to_bar_start+rip.selected_time_range.start-rip.selected_bar_range.start, to_rip)
						new_notes.stretch(end=to_bar_end+rip.selected_time_range.end-rip.selected_bar_range.end)
				else:	# "time"
					if contents.get() == "all":
						new_notes = rip.selected_time_range.notes.copy_to(to_bar_start, to_rip)
						new_notes.stretch(duration=to_bar_duration)
					else:	# "selection"
						new_notes = selected_notes.copy_to(to_bar_start, to_rip)
						new_notes.stretch(duration=to_bar_duration)
				
				# Set duration of any added bars to same as first
				for bar_index in range(orig_n_bars + 1, len(to_rip.bars) + 1):
					to_rip.bar(bar_index).stretch(duration=to_bar_duration)
					
		# Show first rip updated
		for to_rip in rips:
			infinity.current_view.rip = to_rip
			break
					
		# Close window when finished
		#window.destroy()
	
	# Add Apply button
	apply_button = window.add_button(text=" Loop ")
	apply_button.grid(column=1, row=4, padx=16, pady=pad_y, sticky=(E))
	apply_button.bind("<Button>", apply_loop)
	loops_entry.bind("<Return>", apply_loop)
	
	# Bind window destroy event to the window_destroy function to
	# save settings when window closed
	def window_destroy(event):
		if event.widget != window: return
		settings.save()
	window.bind("<Destroy>", window_destroy)
    